package com.grt.condify.reflect;

import com.grt.condify.common.converter.FieldStringToFieldConverter;
import com.grt.condify.common.converter.IConverter;
import com.grt.condify.common.Reports;
import com.grt.condify.common.filter.SimpleFilter;
import com.grt.condify.utils.StringUtil;
import io.vavr.control.Option;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

import static com.grt.condify.reflect.ClassUtils.addDefaultConstructor;
import static com.grt.condify.reflect.ClassUtils.getAllFieldsWithFilter;
import static com.grt.condify.utils.PreConditionCheck.checkNotNull;


/**
 * 给指定class进行解析，获得其最常用的方法反射方法，
 * 包括：构造函授，getter，setter，以及可能的Annotation，并且提供机制能够缓存该内容
 * <p>
 * 所有被解析的类都必须按照POJO的样式包含setter和getter方法,并且命名规则为驼峰命名法
 * <p>
 */
public class ClassInfo {

    /**
     * 支持多并发的Map来满足高并发带来的缓存读取
     */
    private static final Map<Class<?>, ClassInfo> CLASS_CACHE = new ConcurrentHashMap<>();

    public static boolean isCache = true;

    private Constructor defaultConstructor = null;

    private List<Annotation> annotationsOfClass = new ArrayList<>(); //记录类的Annotation

    private Class<?> clazz = null;
    private String className = null;

    public static ClassInfo forName(Class<?> clazz) throws RuntimeException {
        checkNotNull(clazz, "can not parse the specified class because it's null !");
        if (isCache) {
            if (CLASS_CACHE.containsKey(clazz)) {
                return CLASS_CACHE.get(clazz);
            } else {
                ClassInfo instance = new ClassInfo(clazz);
                CLASS_CACHE.put(clazz, instance);
                return instance;
            }
        } else {
            return new ClassInfo(clazz);
        }

    }

    public static void setCache(boolean doCached) {
        isCache = doCached;
    }

    private Class2MehtodInNonStaticCache setterCache = new Class2MehtodInNonStaticCache();

    private Class2MehtodInNonStaticCache getterCache = new Class2MehtodInNonStaticCache();

    private Class2FieldInNonStaticCache fieldCache = new Class2FieldInNonStaticCache();

    private Class2AnnotationInNonStaticCache annotationCache = new Class2AnnotationInNonStaticCache();

    private Class2Annotation2FieldInNoStaticCache annotation2FieldInNoStaticCache = new Class2Annotation2FieldInNoStaticCache();

    /**
     * 解析class,如果字段包含有setter和getter或有Annotation,我们就会保留这些字段
     *
     * @param clazz
     */
    private ClassInfo(final Class<?> clazz) throws RuntimeException {

        this.clazz = checkNotNull(clazz);
        this.className = clazz.getName();
        Reports reporter = Reports.createNewReports(ClassInfo.class.getName());
        try {
            defaultConstructor = addDefaultConstructor(clazz);
            //获得所有的字段,如果字典包含有setter和getter或有Annotation,我们就会保留这些字段
            final Map<Field, Method> setters = new HashMap<>();
            final Map<Field, Method> getters = new HashMap<>();
            final Map<Field, List<Annotation>> fieldAnnotations = new HashMap<>();
            final Map<Class<? extends Annotation>, List<String>> annotationFieldNamesMap = new HashMap<>();
            //存储注解和field属性的关系
            final Map<Class<? extends Annotation>, List<Field>> annotation2FieldsMap = new HashMap<>();
            final List<Field> filteredFields = getAllFieldsWithFilter(clazz,
                    //对字段进行拦截处理,舍弃不符合要求的字段
                    new SimpleFilter<Field>() {

                        private boolean hasSetterOrGetter(Field field) {
                            final String fieldName = field.getName();
                            //得到字段的Getter和Setter,查找包含对父类的查找
                            Option<Method> setterMethod = ClassUtils.getMethodIncludingSupers(
                                    clazz, "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1), field.getType());
                            //由于boolean类型的get方法根据POJO规则,应该是is开头,因此根据字段类型进行getter的方法匹配
                            Option<Method> getterMethod = (field.getType() == boolean.class) ?
                                    ClassUtils.getMethodIncludingSupers(clazz, "is" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1), null) :
                                    ClassUtils.getMethodIncludingSupers(clazz, "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1), null);
                            if (!setterMethod.isEmpty()) {
                                setters.put(field, setterMethod.get());
                            }
                            if (!getterMethod.isEmpty()) {
                                getters.put(field, getterMethod.get());
                            }
                            return !(setterMethod.isEmpty() && getterMethod.isEmpty()); //只有都不存在才会返回false
                        }

                        private boolean hasAnnotation(Class<?> clazz, Field field) {

                            //得到字段的注释信息
                            if (field.getAnnotations().length > 0) {
                                if (field.getDeclaredAnnotations().length > 0) {
                                    List<Annotation> annotations = new ArrayList<>();
                                    Collections.addAll(annotations, field.getDeclaredAnnotations());
                                    fieldAnnotations.put(field, annotations);
                                    //将注解进行封装
                                    for (Annotation one : annotations) {
                                        if (annotationFieldNamesMap.containsKey(one.annotationType())) {
                                            annotationFieldNamesMap.get(one.annotationType()).add(field.getName());
                                            annotation2FieldsMap.get(one.annotationType()).add(field);
                                        } else {
                                            List<String> fieldNames = new ArrayList<String>();
                                            List<Field> fieldList = new ArrayList<>();
                                            fieldNames.add(field.getName());
                                            fieldList.add(field);
                                            annotationFieldNamesMap.put(one.annotationType(), fieldNames);
                                            annotation2FieldsMap.put(one.annotationType(), fieldList);
                                        }
                                    }
                                }
                                return true;
                            } else {
                                return clazz.getSuperclass() != null ? hasAnnotation(clazz.getSuperclass(), field) : false;
                            }

                        }

                        /**
                         * 执行当前命中的处理内容
                         *
                         * @param field 当前执行的处理内容
                         * @throws RuntimeException 执行处理过程中出现问题抛出运行异常
                         */
                        @Override
                        public void doFilter(Field field) throws RuntimeException {
                            boolean hasAnnotation = hasAnnotation(clazz, field);
                            boolean hasSetterOrGetter = hasSetterOrGetter(field);
                            if (hasAnnotation || hasSetterOrGetter) {
                                hitOne = field;
                            }
                        }
                    });
            for (Field cachedField : filteredFields) {
                //解析字段封装对应的Setter
                addSetMethod(cachedField, setters.get(cachedField));
                //解析字段封装对应的Getter
                addGetMethod(cachedField, getters.get(cachedField));
                //解析字段封装对应的Annotation
                if (fieldAnnotations.containsKey(cachedField)) {
                    addAnnotationOfField(cachedField, fieldAnnotations.get(cachedField));
                }
            }
            //添加annotation的缓存
            annotationCache.cache(className, annotationFieldNamesMap);
            annotation2FieldInNoStaticCache.cache(className, annotation2FieldsMap);
            //获得类的Annotation
            if (clazz.getDeclaredAnnotations().length > 0) {
                Collections.addAll(annotationsOfClass, clazz.getDeclaredAnnotations());
            }
        } catch (SecurityException ex) {
            reporter.error(ex, 10);
            throw new RuntimeException(String.format("[%s] can not access private or protected method ! The error information is [%s]",
                    clazz.getName(), reporter.reportFailInfoAsJson()));
        } catch (Exception ex) {
            reporter.error(ex, 10);
            throw new RuntimeException(String.format("It happened some un-expect exceptions when parsing [%s] ! The error information is [%s]",
                    clazz.getName(), reporter.reportFailInfoAsJson()));
        }
    }

    private void addSetMethod(Field field, Method method) {
        final String fieldName = field.getName();
        Map<String, Method> newPair = new HashMap<>();
        newPair.put(fieldName, method);
        setterCache.cache(className, newPair, true);  //直接覆盖
    }

    private void addGetMethod(Field field, Method method) {
        final String fieldName = field.getName();
        Map<String, Method> newPair = new HashMap<>();
        newPair.put(fieldName, method);
        getterCache.cache(className, newPair, true);  //直接覆盖
    }

    private void addAnnotationOfField(Field field, List<Annotation> annotations) {
        Map<Field, List<Annotation>> fieldAnnotationsMap = new HashMap<>();
        fieldAnnotationsMap.put(field, annotations);
        fieldCache.cache(className, fieldAnnotationsMap);
    }

    /**
     * 获得该类所有的get方法
     *
     * @return
     */
    public List<Method> getters() {
        return getterCache.getValueList(className);
    }

    /**
     * 获得该类所有的set方法
     *
     * @return
     */
    public List<Method> setters() {
        return setterCache.getValueList(className);
    }

    /**
     * 获得指定字段对应的getter方法名
     *
     * @param fieldName
     * @return
     * @throws IllegalAccessException 如果指定的字段不存在,抛出该异常
     */
    public Method returnSpecifiedGetter(String fieldName) throws NullPointerException {
        if (StringUtil.isEmpty(fieldName)) {
            throw new NullPointerException(String.format("[%s] doesn't have the specified field : [%s]", className, fieldName));
        }
        return getterCache.searchMethodIn(className, fieldName);
    }

    /**
     * 获得指定字段对应的setter方法名
     *
     * @param fieldName
     * @return
     * @throws IllegalAccessException 如果指定的字段不存在,抛出该异常
     */
    public Method returnSpecifiedSetter(String fieldName) throws NullPointerException {
        if (StringUtil.isEmpty(fieldName)) {
            throw new NullPointerException(String.format("[%s] doesn't have the specified field : [%s]", className, fieldName));
        }
        return setterCache.searchMethodIn(className, fieldName);
    }

    /**
     * 得到所有的Annotations,这里由于一个字段可能有多个Annotation,所以返回的是Annotation的列表的列表
     *
     * @return
     */
    public List<List<Annotation>> getAnnotations() {
        return fieldCache.getValueList(className);
    }

    /**
     * 获得指定字段对应的Annotation
     *
     * @param field
     * @return
     */
    public List<Annotation> getSpecifiedFieldAnnotation(Field field) {
        checkNotNull("The specified field is empty when getting its annotations !");
        return fieldCache.getAnnotationOf(className, field);
    }

    /**
     * 获得指定字段指定类型的Annotation
     *
     * @param field           指定的字段
     * @param annotationClass 指定的Annotation
     * @param <T>
     * @return
     */
    public <T extends Annotation> List<T> getSpecifiedFieldAnnotationAs(Field field, Class<T> annotationClass) {
        List<Annotation> allAnnotation = getSpecifiedFieldAnnotation(field);
        if (allAnnotation == null || allAnnotation.size() <= 0) {
            return null;
        }
        List<T> listOfT = new ArrayList<>();
        for (Annotation annotation : allAnnotation) {
            if (annotationClass.isInstance(annotation)) {
                listOfT.add((T) annotation);
            }
        }
        return listOfT;
    }

    /**
     * 获得指定字段指定类型的Annotation
     *
     * @param field           指定的字段
     * @param annotationClass 指定的Annotation
     * @param <T>
     * @return
     */
    public <T extends Annotation> List<T> getSpecifiedFieldAnnotationAs(String field, Class<T> annotationClass) {
        IConverter<String, Field> converter = new FieldStringToFieldConverter(clazz);
        Field currField = converter.convert(field);
        if (currField == null) {
            return null;
        }
        List<Annotation> allAnnotation = getSpecifiedFieldAnnotation(currField);
        if (allAnnotation == null || allAnnotation.size() <= 0) {
            return null;
        }
        List<T> listOfT = new ArrayList<>();
        for (Annotation annotation : allAnnotation) {
            if (annotationClass.isInstance(annotation)) {
                listOfT.add((T) annotation);
            }
        }
        return listOfT;
    }

    /**
     * 传入指定的Annotation,并获得该Annotation对应的字段名称列表
     *
     * @param annotationClass
     * @return
     */
    public List<String> getFieldByAnnotation(Class<? extends Annotation> annotationClass) {
        checkNotNull(annotationClass, "The specified annotation class is null when getting its field!");
        return annotationCache.getFieldsWithAnnotation(className, annotationClass);
    }

    /**
     * 传入指定的Annotation,并获得该Annotation对应的字段名称列表
     *
     * @param annotationClass
     * @return
     */
    public List<Field> getFieldsByAnnotation(Class<? extends Annotation> annotationClass) {
        checkNotNull(annotationClass, "The specified annotation class is null when getting its field!");
        return annotation2FieldInNoStaticCache.getFieldsWithAnnotation(className, annotationClass);
    }

    /**
     * 传入指定的Annotation,并获得该Annotation对应的类上的Annotation
     *
     * @param annotation
     * @return
     */
    public <T extends Annotation> List<T> getClassAnnotation(Class<? extends Annotation> annotation) {
        List<T> listOfT = new ArrayList<>();
        for (Annotation annotationclass : annotationsOfClass
        ) {
            if (annotation.isInstance(annotationclass)) {
                listOfT.add((T) annotationclass);
            }
        }
        return listOfT;
    }

    /**
     * 传入指定的Annotation,并获得该Annotation对应的类上的Annotation List
     *
     * @param annotation
     * @return
     */
    public <T extends Annotation> Option<List<T>> getAnnotationsOnClass(Class<? extends Annotation> annotation) {
        List<T> listOfT = new ArrayList<>();
        for (Annotation annotationclass : annotationsOfClass
        ) {
            if (annotation.isInstance(annotationclass)) {
                listOfT.add((T) annotationclass);
            }
        }
        return Option.of(listOfT);
    }

    /**
     * 传入指定的Annotation,并获得该Annotation对应的类上的Annotation
     *
     * @param annotation
     * @return
     */
    public <T extends Annotation> Option<T> getAnnotationOnClass(Class<? extends Annotation> annotation) {
        for (Annotation annotationclass : annotationsOfClass
        ) {
            if (annotation.isInstance(annotationclass)) {
                return (Option<T>) Option.of(annotationclass);
            }
        }
        return Option.of(null);
    }

    /**
     * 传入指定注释名,并获得该Annotation对应的类上的Annotation List
     *
     * @param annotationName
     * @return
     */
    public <T extends Annotation> Option<List<T>> getAnnotationsOnClass(String annotationName, Boolean isCaseSensitive) {
        List<T> listOfT = new ArrayList<>();
        for (Annotation annotationclass : annotationsOfClass
        ) {
            String sAnnotationName = annotationclass.annotationType().getSimpleName();
            if (isCaseSensitive && sAnnotationName.contains(annotationName)
                    ||
                    !isCaseSensitive && sAnnotationName.toUpperCase().contains(annotationName.toUpperCase())) {
                listOfT.add((T) annotationclass);
            }
        }
        return Option.of(listOfT);
    }

    /**
     * 传入指定注释名,并获得该Annotation对应的类上的Annotation
     *
     * @param annotationName
     * @return
     */
    public <T extends Annotation> Option<T> getAnnotationOnClass(String annotationName, Boolean isCaseSensitive) {
        for (Annotation annotationclass : annotationsOfClass
        ) {
            String sAnnotationName = annotationclass.annotationType().getSimpleName();
            if (isCaseSensitive && sAnnotationName.contains(annotationName)
                    ||
                    !isCaseSensitive && sAnnotationName.toUpperCase().contains(annotationName.toUpperCase())) {
                return (Option<T>) Option.of(annotationclass);
            }
        }
        return Option.of(null);
    }

    public Constructor getDefaultConstructor() {
        return defaultConstructor;
    }

}
